package org.xlp.mvc.context;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xlp.assertion.AssertUtils;
import org.xlp.bean.annotation.Component;
import org.xlp.bean.base.IBeanDefinition;
import org.xlp.bean.impl.ComponentAnnotationBeanDefinitionCreator;
import org.xlp.mvc.annotation.CBController;
import org.xlp.mvc.annotation.Controller;
import org.xlp.mvc.annotation.Service;
import org.xlp.mvc.definition.ControllerAnnotationBeanDefinitionCreator;
import org.xlp.mvc.definition.ServiceAnnotationBeanDefinitionCreator;
import org.xlp.mvc.mapping.RequestMappingMap;
import org.xlp.mvc.register.BeanDefinitionCreatorRegister;
import org.xlp.scanner.pkg.ClassPathPkgScanner;
import org.xlp.scanner.pkg.ConfigurationScannerPkg;
import org.xlp.scanner.pkg.ScannerAnnotationParser;
import org.xlp.scanner.pkg.ScannerPkg;
import org.xlp.utils.ClassLoaderUtils;
import org.xlp.utils.XLPArrayUtil;
import org.xlp.utils.XLPStringUtil;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * @author xlp
 * created by xlp 20230828
 * 上下文容器
 */
public class ApplicationContext extends AbstractApplicationContext{
    private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationContext.class);

    /**
     * 包扫描器
     */
    private ScannerPkg scannerPkg;


    /**
     * 构造函数
     */
    public ApplicationContext() {
        registerBeanDefinitionCreator();
    }

    /**
     * 构造函数
     *
     * @param classLoader
     * @param packages    要扫描的包路径
     */
    public ApplicationContext(ClassLoader classLoader, String[] packages) {
        super(classLoader, packages);
        registerBeanDefinitionCreator();
    }

    /**
     * 构造函数
     *
     * @param classLoader
     * @param scannerPkg    包扫描器
     */
    public ApplicationContext(ClassLoader classLoader, ScannerPkg scannerPkg) {
        this(classLoader, (String[])null);
        this.scannerPkg = scannerPkg;
    }

    /**
     * 构造函数
     *
     * @param classLoader
     * @param packages    要扫描的包路径
     * @param scannerPkg    包扫描器
     */
    public ApplicationContext(ClassLoader classLoader, String[] packages, ScannerPkg scannerPkg) {
        this(classLoader, packages);
        this.scannerPkg = scannerPkg;
    }

    /**
     * 获取包扫描去
     * @return
     */
    public ScannerPkg getScannerPkg() {
        return scannerPkg == null ? new ClassPathPkgScanner() : scannerPkg;
    }

    /**
     * 向容器中注册bean定义创建器
     */
    protected void registerBeanDefinitionCreator(){
        //向bean定义注册相应的创建器
        BeanDefinitionCreatorRegister.register(Component.class, new ComponentAnnotationBeanDefinitionCreator());
        BeanDefinitionCreatorRegister.register(Service.class, new ServiceAnnotationBeanDefinitionCreator());
        BeanDefinitionCreatorRegister.register(Controller.class, new ControllerAnnotationBeanDefinitionCreator());
        BeanDefinitionCreatorRegister.register(CBController.class, new ControllerAnnotationBeanDefinitionCreator());
    }

    /**
     * 完成创建上下文对象
     *
     * @return
     */
    @Override
    public ApplicationContext complete() throws ApplicationContextException {
        Set<String> parseClasses = getParseClasses();
        if (LOGGER.isDebugEnabled()){
            LOGGER.debug("扫描到符合的类个数是：" + parseClasses.size());
            LOGGER.debug("扫描出的类列表如下：" + parseClasses.toString());
        }

        Set<Class<?>> classes = new HashSet<>(parseClasses.size());
        for (String cn : parseClasses) {
            try {
                Class<?> clazz = forClass(cn);
                classes.add(clazz);
            } catch (ClassNotFoundException e) {
                if (LOGGER.isWarnEnabled()){
                    LOGGER.warn("创建类名为【" + cn + "】的Class对象失败", e);
                }
            }
        }

        //过滤掉不修要的类型
        classes = filterClasses(classes);

        //把扫描出的class转换超bean定义对象
        covertBeanDefinitions(classes);

        //通过bean定义创建出非懒加载的单例bean实例对象
        createBeans();

        //对扫描出来的类进行扩展处理
        afterProcess(classes);

        return this;
    }

    /**
     * 根据条件过滤一下不修要的类
     * @param classes
     * @return 返回过滤后的类型集合
     */
    protected Set<Class<?>> filterClasses(Set<Class<?>> classes) {
        return classes;
    }

    /**
     * 对扫描出来的类进行扩展处理
     * @param classes
     */
    protected void afterProcess(Set<Class<?>> classes) {
        //过滤出所有有Controller注解的类
        Set<Class<?>> controllerClass = new HashSet<>();
        classes.forEach(c -> {
            if (c.getAnnotation(Controller.class) != null
                || c.getAnnotation(CBController.class) != null) controllerClass.add(c);
        });

        //处理所有Controller类，并放入RequestMappingMap缓存里
        RequestMappingMap.loadAllController(controllerClass);
    }

    /**
     * 把扫描出的class转换超bean定义对象
     * @param classes
     */
    protected void covertBeanDefinitions(Set<Class<?>> classes) {
        Set<Class<? extends Annotation>> annotationClasses = BeanDefinitionCreatorRegister.getAnnotationKeys();
        classes.forEach(c -> {
            IBeanDefinition beanDefinition = getBeanDefinition(c, annotationClasses);
            if (beanDefinition != null) addBeanDefinition(beanDefinition);
        });
    }

    private IBeanDefinition getBeanDefinition(Class<?> c,  Set<Class<? extends Annotation>> annotationClasses) {
        for (Class<? extends Annotation> annotationClass : annotationClasses) {
            Annotation annotation = c.getAnnotation(annotationClass);
            if (annotation != null) return BeanDefinitionCreatorRegister.getBeanDefinitionCreatorByAnnotation(annotationClass)
                    .createBeanDefinition(c);
        }
        return null;
    }

    /**
     * 通过给定的类名获取对应的Class对象
     * @param className
     * @return
     */
    private Class<?> forClass(String className) throws ClassNotFoundException {
        ClassLoader classLoader = getClassLoader();
        classLoader = classLoader == null ? ClassLoaderUtils.getDefaultClassLoader() : classLoader;
        return Class.forName(className, false, classLoader);
    }

    /**
     * 获取要解析后的所有类型
     * @return
     */
    private Set<String> getParseClasses() throws ApplicationContextException {
        ScannerPkg scannerPkg = getScannerPkg();
        if (scannerPkg instanceof ConfigurationScannerPkg){
            if (LOGGER.isDebugEnabled()){
                LOGGER.debug("通过配置类进行扫描出相应包下的所有类名");
            }
            try {
                return ((ConfigurationScannerPkg)scannerPkg).scanner();
            } catch (IOException e) {
                throw new ApplicationContextException(e);
            }
        }
        String[] packages = getPackages();
        try {
            if (LOGGER.isDebugEnabled()){
                LOGGER.debug("通过给定包扫描出相应包下的所有类名，给定包是：" + Arrays.toString(packages));
            }
            if (XLPArrayUtil.isEmpty(packages)) {
                return scannerPkg.scanner(XLPStringUtil.EMPTY);
            } else {
                Set<String> result = new HashSet<>();
                for (String p : packages) {
                    result.addAll(scannerPkg.scanner(p));
                }
                return result;
            }
        } catch (IOException e) {
            throw new ApplicationContextException(e);
        }
    }

    /**
     * 创建上下文
     * @param parseClass 要解析的配置类，通过该配置了加载到哪些包需要扫描
     * @return 上下文
     * @throws NullPointerException 假如参数为null，则抛出该异常
     */
    public static ApplicationContext create(Class<?> parseClass) throws ApplicationContextException {
        return create(parseClass, ApplicationContext.class.getClassLoader());
    }

    /**
     * 创建上下文
     * @param parseClass 要解析的配置类，通过该配置了加载到哪些包需要扫描
     * @return 上下文
     * @throws NullPointerException 假如第一个参数为null，则抛出该异常
     */
    public static ApplicationContext create(Class<?> parseClass, ClassLoader classLoader)
            throws ApplicationContextException {
        AssertUtils.isNotNull(parseClass, "parseClass parameter must not be null!");
        ApplicationContext context = new ApplicationContext(classLoader, new ScannerAnnotationParser(classLoader, parseClass))
                .complete();
        //把创建好的上下文放入缓存中
        ApplicationContextUtils.pushContext(context);
        return context;
    }
}
